#pragma once

#include <cmath> //for sqrt

struct vec3f final
{
	static const size_t arity = 3;

	float& operator[](const size_t index) { return *(&x + index); }
	float operator[](const size_t index) const { return *(&x + index); }
	bool operator==(const vec3f rhs) = delete;

	float x;
	float y;
	float z;
};

inline vec3f vec3fc(const float x, const float y, const float z)
{
	vec3f pnt = { x, y, z };
	return pnt;
}

inline vec3f zero_vec3f()
{
	return vec3fc(0, 0, 0);
}

inline vec3f operator-(const vec3f lhs, const vec3f rhs)
{
	return vec3fc(lhs[0] - rhs[0], lhs[1] - rhs[1], lhs[2] - rhs[2]);
}

inline vec3f operator+(const vec3f lhs, const vec3f rhs)
{
	return vec3fc(lhs[0] + rhs[0], lhs[1] + rhs[1], lhs[2] + rhs[2]);
}

inline vec3f operator*(const vec3f lhs, const float s)
{
	return vec3fc(lhs[0] * s, lhs[1] * s, lhs[2] * s);
}

inline vec3f operator*(const float s, const vec3f rhs)
{
	return vec3fc(rhs[0] * s, rhs[1] * s, rhs[2] * s);;
}

inline vec3f& operator+=(vec3f& lhs, const vec3f rhs)
{
	lhs[0] += rhs[0];
	lhs[1] += rhs[1];
	lhs[2] += rhs[2];
	return lhs;
}

inline vec3f& operator-=(vec3f& lhs, const vec3f rhs)
{
	lhs[0] -= rhs[0];
	lhs[1] -= rhs[1];
	lhs[2] -= rhs[2];
	return lhs;
}

inline vec3f& operator-(vec3f& lhs)
{
	lhs[0] = -lhs[0];
	lhs[1] = -lhs[1];
	lhs[2] = -lhs[2];
	return lhs;
}

inline bool e_null_vec(const vec3f V)
{
	return V[0] == 0 && V[1] == 0 && V[2] == 0;
}

inline bool g_null_vec(const vec3f V)
{
	return V[0] > 0.f || V[1] > 0.f || V[2] > 0.f;
}

inline bool ge_null_vec(const vec3f V)
{
	return V[0] >= 0.f || V[1] >= 0.f || V[2] >= 0.f;
}

inline vec3f mul_vec(const vec3f lhs, const vec3f rhs)
{
	return vec3fc(lhs[0] * rhs[0], lhs[1] * rhs[1], lhs[2] * rhs[2]);
}

inline float dot(const vec3f lhs, const vec3f rhs)
{
	float d = lhs[0] * rhs[0] + lhs[1] * rhs[1] + lhs[2] * rhs[2];
	return d;
}

inline vec3f reflect(const vec3f V, const vec3f N)
{
	return (N * dot(V, N) - V) * 2.f + V;
}

inline vec3f cross(const vec3f lhs, const vec3f rhs)
{
	return vec3fc(lhs.y * rhs.z - lhs.z * rhs.y, lhs.z * rhs.x - lhs.x * rhs.z, lhs.x * rhs.y - lhs.y * rhs.x);
}

inline float length(const vec3f v)
{
	float len = sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]);
	return len;
}

inline vec3f normalize(const vec3f v)
{
	vec3f ret = v;

	float len = sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]);

	if (len > 1e-08)
	{
		float inv_len = 1.f / len;
		ret[0] *= inv_len;
		ret[1] *= inv_len;
		ret[2] *= inv_len;
	}

	return ret;
}

inline vec3f lerp(const vec3f a, const vec3f b, const float alpha)
{
	return a*(1 - alpha) + b*alpha;
}